/*
 * Copyright (C) 2012 Andrea Parodi
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package container;

import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;

import java.util.ArrayList;

/**
 * User: andrea.parodi
 * Date: 26/04/12
 * Time: 11.21
 */
public class ContainerNumbersParser {


    private final ArrayList<String> results = new ArrayList<String>();

    private abstract class ParserState {
        private final String partialCntrNumber;

        ParserState(String partialCntrNumber) {
            Preconditions.checkNotNull(partialCntrNumber, "partialCntrNumber param");
            this.partialCntrNumber = partialCntrNumber;
        }

        abstract ParserState nextState(char nextChar);

        String getPartialCntrNumber() {
            return partialCntrNumber;
        }
    }

    private class WithinLettersState extends ParserState {

        WithinLettersState(String partialCntrNumber) {
            super(partialCntrNumber);
        }


        @Override ParserState nextState(char nextChar) {
            if (!Character.isLetter(nextChar))
                return new NoCntrNumberState();

            final String cntrNumber = getPartialCntrNumber();
            if (cntrNumber.length() == 3)
                return new LastLetterState(cntrNumber + nextChar);
            else
                return new WithinLettersState(cntrNumber + nextChar);
        }
    }

    private class LastLetterState extends ParserState {
        private final boolean alreadySeparated;

        LastLetterState(String partialCntrNumber) {
            this(partialCntrNumber, false);
        }

        LastLetterState(String partialCntrNumber,boolean alreadySeparated) {
            super(partialCntrNumber);
            this.alreadySeparated = alreadySeparated;
        }


        @Override ParserState nextState(char nextChar) {
            String partialCntrNumber = getPartialCntrNumber();
            if (Character.isDigit(nextChar)) {

                return new WithinDigitsState(partialCntrNumber + nextChar);
            }
            if (isSeparator(nextChar) && !alreadySeparated)
                return new LastLetterState(partialCntrNumber,true);


            return new NoCntrNumberState();
        }
    }

    private class NoCntrNumberState extends ParserState {

        NoCntrNumberState() {
            super("");
        }


        @Override ParserState nextState(char nextChar) {
            if (Character.isLetter(nextChar))
                return new WithinLettersState(String.valueOf(nextChar));
            return this;
        }
    }

    private class CntrNumberFound extends ParserState {

        CntrNumberFound(String partialCntrNumber) {
            super(partialCntrNumber);
        }


        @Override ParserState nextState(char nextChar) {
            if (isSeparator(nextChar)) {
                results.add(getPartialCntrNumber());
            }


            return new NoCntrNumberState();
        }
    }

    public static boolean isSeparator(char c) {
        return Character.isWhitespace(c) || "-/+\\,.;=()[]{}&%<>\r\n\t".indexOf(c) != -1;
    }

    private class WithinDigitsState extends ParserState {

        WithinDigitsState(String partialCntrNumber) {
            super(partialCntrNumber);
        }

        @Override ParserState nextState(char nextChar) {
            final String cntrNumber = getPartialCntrNumber();
            if (Character.isDigit(nextChar)) {
                if (cntrNumber.length() < 9)
                    return new WithinDigitsState(cntrNumber + nextChar);
                else if (cntrNumber.length() == 9)
                    return new CheckDigitState(cntrNumber + nextChar);

            }

            return new NoCntrNumberState();
        }
    }

    private class CheckDigitState extends ParserState {
        private final boolean alreadySeparated;
        CheckDigitState(String partialCntrNumber) {
            this(partialCntrNumber,false);
        }
        CheckDigitState(String partialCntrNumber, boolean alreadySeparated) {
            super(partialCntrNumber);
            this.alreadySeparated = alreadySeparated;
        }

        @Override ParserState nextState(char nextChar) {
            final String cntrNumber = getPartialCntrNumber();
            if (Character.isDigit(nextChar)) {
                return new CntrNumberFound(cntrNumber + nextChar);
            }
            if (isSeparator(nextChar) && !alreadySeparated)
                return new CheckDigitState(cntrNumber,true);

            return new NoCntrNumberState();
        }
    }


    private ParserState currentState = new NoCntrNumberState();

    public ImmutableList<String> parseCntrNumbers(String textToParse) {
        results.clear();
        for (int i = 0; i < textToParse.length(); i++) {
            if (i==128)
                System.out.print("");
            char c = textToParse.charAt(i);
            currentState = currentState.nextState(c);
        }
        if (currentState instanceof CntrNumberFound)
            currentState.nextState(' ');
        return ImmutableList.copyOf(results);
    }


}
